﻿// // ******************************************************************
// //       /\ /|       @file       SeriesHelper.cs
// //       \ V/        @brief      await29
// //       | "")       @author     Await, 285283010@qq.com
// //       /  |
// //      /  \\        @Modified   2025-01-04 19:01
// //    *(__\_\        @Copyright  Copyright (c) , 2025-01-04 17:01 Await
// // ******************************************************************

using System.Collections.Concurrent;
using AutomaticSubtitles.Service.DataAccess.Model;

namespace AutomaticSubtitles.Service.Infrastructure.Helper;

public class SeriesHelper
{
    /// <summary>
    ///     获取电影的所有字幕文件
    /// </summary>
    /// <param name="itemId"></param>
    /// <param name="itemPath"></param>
    /// <param name="wantedExts"></param>
    /// <param name="isAll"></param>
    /// <returns></returns>
    public static async Task<List<Auto_Subtitle>?> GetMovieSubtitleFiles(string itemId, string itemPath, Dictionary<string, bool> wantedExts, bool isAll)
    {
        var dir = Path.GetDirectoryName(itemPath);
        if (dir.IsNullOrWhiteSpace()) return null;
        var fileName = Path.GetFileNameWithoutExtension(itemPath).ToLower();
        var matchedSubs = Directory.GetFiles(dir).Select(curFile => GetLocalSubtitles(itemId, curFile, fileName)).OfType<Auto_Subtitle>().ToList();
        var readSubtitlesMediaFiles = await VideoFileUtils.ReadSubtitlesMediaFiles(itemPath, true, wantedExts);
        if (readSubtitlesMediaFiles == null)
        {
            //追加默认字幕
            var subtitlesMediaFiles = await VideoFileUtils.ReadSubtitlesMediaFiles(itemPath, true, wantedExts, true);
            if (subtitlesMediaFiles == null) return matchedSubs;
            foreach (var subtitlesMediaFile in subtitlesMediaFiles)
                matchedSubs.AddRange(subtitlesMediaFile.Value.Select(se => new Auto_Subtitle(BasicsHelper.GetId())
                {
                    Name = Path.GetFileName(se.Item2),
                    Path = se.Item2,
                    VideoId = itemId,
                    IsDefault = true,
                    Index = se.Item1,
                    IsMediaFile = true,
                    Language = subtitlesMediaFile.Key,
                    Size = FileHelper.GetFileSize(se.Item2, 1)
                }));
            return matchedSubs;
        }

        foreach (var item in readSubtitlesMediaFiles)
        {
            var isMediaFile = true;
            if (wantedExts.TryGetValue(item.Key, out var value))
                if (value)
                    isMediaFile = false;
            matchedSubs.AddRange(item.Value.Select(se => new Auto_Subtitle(BasicsHelper.GetId())
            {
                Name = Path.GetFileName(se.Item2),
                Path = isMediaFile ? se.Item2 : $"{itemPath}.cache.{se.Item2}",
                VideoId = itemId,
                Language = item.Key,
                Index = se.Item1,
                IsMediaFile = isMediaFile,
                Size = isMediaFile ? 0 : FileHelper.GetFileSize(se.Item2, 1)
            }));
        }


        // foreach (var keyValuePair in wantedExts)
        // {
        //     readSubtitlesMediaFiles.TryGetValue(keyValuePair.Key, out var data);
        //     if (data == null) continue;
        //     if (!keyValuePair.Value)
        //     {
        //         var enumerable = data.Select(x => new Auto_Subtitle(BasicsHelper.GetId())
        //         {
        //             Name = Path.GetFileName(x),
        //             Path = $"{itemPath}.cache.{x}",
        //             VideoId = itemId,
        //             Language = keyValuePair.Key,
        //             IsMediaFile = true,
        //             Size = FileHelper.GetFileSize(itemPath, 1)
        //         });
        //         matchedSubs.AddRange(enumerable);
        //         continue;
        //     }
        //
        //     matchedSubs.AddRange(data.Select(se => new Auto_Subtitle(BasicsHelper.GetId())
        //     {
        //         Name = Path.GetFileName(se),
        //         Path = se,
        //         VideoId = itemId,
        //         Language = keyValuePair.Key,
        //         Size = FileHelper.GetFileSize(se, 1)
        //     }));
        // }

        return matchedSubs;
    }

    /// <summary>
    /// 获取本地文件夹字幕
    /// </summary>
    /// <param name="itemId"></param>
    /// <param name="curFile"></param>
    /// <param name="fileName"></param>
    /// <returns></returns>
    private static Auto_Subtitle? GetLocalSubtitles(string itemId, string curFile, string fileName)
    {
        if (VideoFileUtils.IsSoftConnection(new FileInfo(curFile), curFile)) return null;
        // 判断的时候用小写的，后续重命名的时候用原有的名称
        var nowFileName = Path.GetFileName(curFile).ToLower();
        if (!VideoFileUtils.IsSubExtWanted(Path.GetExtension(nowFileName))) return null;
        // 后缀名得对
        nowFileName = nowFileName.Replace(Common.SrtDefaultKey, "");
        // 字幕文件名应该包含 视频文件名（无后缀）
        if (!nowFileName.StartsWith(fileName)) return null;
        // isStr = true;
        return new Auto_Subtitle(BasicsHelper.GetId())
        {
            // Name = Path.GetFileName(curFile),
            Name = nowFileName,
            Path = curFile,
            VideoId = itemId,
            Language = VideoFileUtils.GetLanguage(curFile),
            Size = FileHelper.GetFileSize(curFile, 1)
        };
    }


    /// <summary>
    /// 获取电视剧中的字幕
    /// </summary>
    /// <param name="itemId"></param>
    /// <param name="itemPath"></param>
    /// <param name="wantedExts"></param>
    /// <param name="isAll"></param>
    /// <returns></returns>
    public static async Task<List<Auto_Subtitle>?> GetTvDramasSubtitleFiles(List<(string, string)> itemPath, Dictionary<string, bool> wantedExts, bool isAll)
    {
        var subtitleFiles = new ConcurrentBag<Auto_Subtitle>();
        //创建多线程任务执行
        // foreach (var item in itemPath)
        var tasks = itemPath.Select(item => Task.Run(async () =>
        {
            var dir = Path.GetDirectoryName(item.Item2);
            if (dir.IsNullOrWhiteSpace()) return;
            // if (dir.IsNullOrWhiteSpace()) continue;
            var fileName = Path.GetFileNameWithoutExtension(item.Item2).ToLower();
            var matchedSubs = Directory.GetFiles(dir).Select(curFile => GetLocalSubtitles(item.Item1, curFile, fileName)).OfType<Auto_Subtitle>().ToList();
            foreach (var autoSubtitle in matchedSubs) subtitleFiles.Add(autoSubtitle);

            var readSubtitlesMediaFiles = await VideoFileUtils.ReadSubtitlesMediaFiles(item.Item2, false, wantedExts);
            if (readSubtitlesMediaFiles == null)
            {
                //追加默认字幕
                var subtitlesMediaFiles = await VideoFileUtils.ReadSubtitlesMediaFiles(item.Item2, false, wantedExts, true);
                if (subtitlesMediaFiles == null) return;
                // if (subtitlesMediaFiles == null) continue;
                foreach (var autoSubtitle in subtitlesMediaFiles.Select(subtitlesMediaFile => subtitlesMediaFile.Value.Select(se => new Auto_Subtitle(BasicsHelper.GetId())
                         {
                             Name = Path.GetFileName(se.Item2),
                             Path = se.Item2,
                             VideoId = item.Item1,
                             IsDefault = true,
                             Index = se.Item1,
                             IsMediaFile = true,
                             Language = subtitlesMediaFile.Key,
                             Size = FileHelper.GetFileSize(se.Item2, 1)
                         })).SelectMany(autoSubtitles => autoSubtitles))
                    subtitleFiles.Add(autoSubtitle);

                return;
                // continue;
            }

            foreach (var itemk in readSubtitlesMediaFiles)
            {
                var isMediaFile = true;
                if (wantedExts.TryGetValue(itemk.Key, out var value))
                    if (value)
                        isMediaFile = false;
                foreach (var autoSubtitle in itemk.Value.Select(se => new Auto_Subtitle(BasicsHelper.GetId())
                         {
                             Name = Path.GetFileName(se.Item2),
                             Path = isMediaFile ? se.Item2 : $"{itemPath}.cache.{se.Item2}",
                             VideoId = item.Item1,
                             Language = itemk.Key,
                             Index = se.Item1,
                             IsMediaFile = isMediaFile,
                             Size = isMediaFile ? 0 : FileHelper.GetFileSize(se.Item2, 1)
                         }))
                    subtitleFiles.Add(autoSubtitle);
            }


            // foreach (var keyValuePair in wantedExts)
            // {
            //     readSubtitlesMediaFiles.TryGetValue(keyValuePair.Key, out var data);
            //     if (data == null) continue;
            //     if (!keyValuePair.Value)
            //     {
            //         var enumerable = data.Select(x => new Auto_Subtitle(BasicsHelper.GetId())
            //         {
            //             Name = Path.GetFileName(x.Item2),
            //             Path = $"{item}.cache.{x}",
            //             VideoId = itemId,
            //             Index = x.Item1,
            //             Language = keyValuePair.Key,
            //             IsMediaFile = true,
            //             Size = FileHelper.GetFileSize(item, 1)
            //         });
            //         foreach (var autoSubtitle in enumerable)
            //             subtitleFiles.Add(autoSubtitle);
            //         continue;
            //     }
            //
            //     foreach (var autoSubtitle in data.Select(se => new Auto_Subtitle(BasicsHelper.GetId())
            //              {
            //                  Name = Path.GetFileName(se.Item2),
            //                  Path = se.Item2,
            //                  VideoId = itemId,
            //                  Index = se.Item1,
            //                  Language = keyValuePair.Key,
            //                  Size = FileHelper.GetFileSize(se.Item2, 1)
            //              }))
            //         subtitleFiles.Add(autoSubtitle);
            // }
        }));
        // }
        await Parallel.ForEachAsync(tasks, new ParallelOptions
        {
            MaxDegreeOfParallelism = 2
        }, async (task, _) => await task);
        // await Task.WhenAll(tasks);

        return subtitleFiles.ToList();
    }

    #region 操作

    /// <summary>
    /// MatchedVideoFileFromDirsAsync 搜索符合后缀名的视频文件
    /// </summary>
    /// <param name="_log"></param>
    /// <param name="dirs"></param>
    /// <param name="noSubPath"></param>
    /// <returns></returns>
    public static async Task<Dictionary<string, List<Auto_Media>>?> MatchedVideoFileFromDirsAsync(ILogger<VideoListHelper> _log, List<string> dirs, List<string>? noSubPath)
    {
        _log.LogInformation("匹配视频文件 开始...");
        var fileFullPathMap = new Dictionary<string, List<Auto_Media>>();
        foreach (var dir in dirs)
        {
            if (dir.IsNullOrWhiteSpace())
            {
                _log.LogError("目录为空");
                continue;
            }

            if (noSubPath != null && noSubPath.Any(x => dir.Contains(x))) continue;
            // if (noSubPath != null && noSubPath.Contains(dir)) continue;
            var matchedVideoFile = await MatchedVideoFileAsync(dir, noSubPath, _log);
            if (matchedVideoFile == null) return null; // 或者抛出异常
            if (!fileFullPathMap.TryAdd(dir, matchedVideoFile))
                fileFullPathMap[dir].AddRange(matchedVideoFile);
        }

        _log.LogInformation("匹配视频文件 结束...");

        return fileFullPathMap;
    }

    /// <summary>
    /// 扫描电视剧
    /// </summary>
    /// <param name="logger"></param>
    /// <param name="dirs"></param>
    /// <param name="noSubPath"></param>
    /// <returns></returns>
    public static async Task<Dictionary<string, List<Auto_Media>>?> GetSeriesListFromDirsAsync(ILogger<VideoListHelper> logger, List<string> dirs, List<string>? noSubPath)
    {
        logger.LogInformation("获取系列列表 开始...");

        var fileFullPathMap = new Dictionary<string, List<Auto_Media>>();

        foreach (var dir in dirs)
        {
            if (dir.IsNullOrWhiteSpace())
            {
                logger.LogError("目录为空");
                continue;
            }

            if (noSubPath != null && noSubPath.Contains(dir)) continue;

            var seriesList = GetSeriesList(logger, dir, noSubPath);
            if (seriesList == null) return null; // 或者抛出异常
            foreach (var keyValuePair in seriesList)
            {
                var mediae = keyValuePair.Value;
                mediae.Add(keyValuePair.Key);
                var autoMediae = mediae.OrderByDescending(x => x.LastWriteTime).ToList();
                if (!fileFullPathMap.TryGetValue(dir, out var value))
                {
                    fileFullPathMap.Add(dir, autoMediae);
                }
                else
                {
                    value.AddRange(autoMediae);
                    fileFullPathMap[dir] = value;
                }
            }

            // var autoMediae = seriesList.Select(oneSeriesRootDir => new Auto_Media(BasicsHelper.GetId())
            // {
            //     Name = Path.GetFileName(oneSeriesRootDir), MediaType = GlobalVariableEnumConfiguration.MediaEnum.电视剧,
            //     MainRootDirFPath = dir,
            //     Path = oneSeriesRootDir,
            //     PosterAddress = FileHelper.GetSeriesPoster(oneSeriesRootDir),
            //     LastWriteTime = File.GetLastWriteTime(oneSeriesRootDir),
            //     Size = FileHelper.GetFolderSize(oneSeriesRootDir)
            // }).OrderByDescending(x => x.LastWriteTime).ToList();
        }

        logger.LogInformation("获取系列列表 结束");
        return fileFullPathMap;
    }

    #endregion

    #region 帮助

    /// <summary>
    ///  GetSeriesList 获取这个目录下的所有文件夹名称，默认为一个连续剧的目录的List
    /// </summary>
    /// <param name="log"></param>
    /// <param name="dir"></param>
    /// <param name="noSubPath"></param>
    /// <returns></returns>
    private static Dictionary<Auto_Media, List<Auto_Media>>? GetSeriesList(ILogger log, string dir, List<string>? noSubPath)
    {
        // 这里实现获取该目录下所有剧集的逻辑
        // 例如，搜索 tvshow.nfo 文件并返回对应的目录列表
        var data = new Dictionary<Auto_Media, List<Auto_Media>>();

        var tvNfOs = TvNfo(dir);
        // foreach (var item in tvNfOs.Select(Path.GetDirectoryName).OfType<string>())
        foreach (var citem in tvNfOs)
        {
            if (noSubPath != null && noSubPath.Any(x => citem.Contains(x))) continue;
            var item = Path.GetDirectoryName(citem)!;
            var autoMedia = new Auto_Media(BasicsHelper.GetId())
            {
                Name = Path.GetFileName(item), MediaType = GlobalVariableEnumConfiguration.MediaEnum.电视剧,
                MainRootDirFPath = dir,
                Path = item,
                IsDisc = true,
                InformationAddress = citem,
                PosterAddress = FileHelper.GetSeriesPoster(item),
                LandscapeAddress = FileHelper.GetSeriesLandscape(item),
                LastWriteTime = File.GetLastWriteTime(item),
                Leve = 0,
                Size = FileHelper.GetFolderSize(item)
            };
            var detailedInformation = GetDetailedInformation(autoMedia.Id, item, noSubPath);
            //TODO： 可能出现重复的文件夹 需要判断
            if (detailedInformation != null && !data.TryAdd(autoMedia, detailedInformation))
                data[autoMedia].AddRange(detailedInformation);
        }

        return data;
    }

    /// <summary>
    /// 返回每一集的详细信息
    /// </summary>
    /// <param name="autoMediaId"></param>
    /// <param name="path"></param>
    /// <param name="noSubPath"></param>
    /// <returns></returns>
    private static List<Auto_Media>? GetDetailedInformation(string autoMediaId, string path, List<string>? noSubPath)
    {
        var data = new List<Auto_Media>();
        var delimiter = Path.DirectorySeparatorChar.ToString();
        var quarterDic = new Dictionary<string, int>();
        if (path.Contains(Common.MetadateSeasonName) || path.Contains("BDMV")) return null;
        var fileNameIsBdmv = VideoFileUtils.FileNameIsBDMV(path);
        if (fileNameIsBdmv.Item1) return null;

        foreach (var directory in Directory.GetDirectories(path))
        {
            var directoryEntries = ReadDir(directory);
            foreach (var item in directoryEntries.Where(x => VideoFileUtils.IsWantedVideoExtDef(x.FullPath)))
            {
                if (noSubPath != null && noSubPath.Any(x => item.FullPath.Contains(x))) continue;
                var directorySeparatorChar = Path.GetDirectoryName(item.FullPath) + delimiter + Path.GetFileName(item.FullPath).Replace(Path.GetExtension(item.FullPath), "");
                var metadateSeasonnNfo = directorySeparatorChar + Common.MetadateSeasonnNfo;
                // var directoryInfo = Directory.GetParent(item.FullPath);
                var directoryInfo = new DirectoryInfo(directory);
                var name = directoryInfo!.Name.Replace(Common.MetadateSeasonName, "");

                //TODO:圆盘文件待处理
                if (!quarterDic.ContainsKey(directory))
                    try
                    {
                        quarterDic.Add(directory, int.Parse(name));
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e);
                        continue;
                    }

                var quarter = quarterDic[directory];
                data.Add(new Auto_Media(BasicsHelper.GetId())
                {
                    ParentLevelId = autoMediaId,
                    Name = Path.GetFileName(item.FullPath),
                    MediaType = GlobalVariableEnumConfiguration.MediaEnum.电视剧,
                    MainRootDirFPath = path,
                    Path = item.FullPath,
                    InformationAddress = directoryEntries.Find(x => x.FullPath == metadateSeasonnNfo)?.FullPath,
                    PosterAddress = FileHelper.GetSeriesPoster(directorySeparatorChar),
                    LandscapeAddress = FileHelper.GetSeriesLandscape(directorySeparatorChar),
                    LastWriteTime = File.GetLastWriteTime(item.FullPath),
                    Leve = 1,
                    Quarter = quarter,
                    Size = FileHelper.GetFileSize(item.FullPath)
                });
            }
        }


        return data;
    }

    private static List<string>? TvNfo(string dir)
    {
        var fileFullPathList = new List<string>();
        var pathSep = Path.DirectorySeparatorChar.ToString();
        var files = ReadDir(dir);
        foreach (var curFile in files)
        {
            var fullPath = dir + pathSep + curFile.Name;
            if (curFile.IsDirectory)
            {
                // 内层的错误就无视了
                var oneList = TvNfo(fullPath);
                if (oneList != null) fileFullPathList.AddRange(oneList);
            }
            else
            {
                // 这里就是文件了
                if (string.Equals(curFile.Name.ToLower(), Common.MetadateTVNfo.ToLower())) fileFullPathList.Add(fullPath);
            }
        }

        return fileFullPathList;
    }


    /// <summary>
    ///  GetSeriesList 获取这个目录下的所有文件夹名称，默认为一个连续剧的目录的List
    /// </summary>
    /// <param name="log"></param>
    /// <param name="dir"></param>
    /// <returns></returns>
    private static List<string>? GetSeriesList2(ILogger log, string dir)
    {
        // 这里实现获取该目录下所有剧集的逻辑
        // 例如，搜索 tvshow.nfo 文件并返回对应的目录列表
        var tvNfOs = TvNfo2(dir);
        return tvNfOs.Select(Path.GetDirectoryName).OfType<string>().ToList();
    }

    private static List<string>? TvNfo2(string dir)
    {
        var fileFullPathList = new List<string>();
        var pathSep = Path.DirectorySeparatorChar.ToString();
        var files = ReadDir(dir);
        foreach (var curFile in files)
        {
            var fullPath = dir + pathSep + curFile.Name;
            if (curFile.IsDirectory)
            {
                // 内层的错误就无视了
                var oneList = TvNfo2(fullPath);
                if (oneList != null) fileFullPathList.AddRange(oneList);
            }
            else
            {
                // 这里就是文件了
                if (string.Equals(curFile.Name.ToLower(), Common.MetadateTVNfo.ToLower())) fileFullPathList.Add(fullPath);
            }
        }

        return fileFullPathList;
    }


    //ReadDir读取指定目录，
    //返回按文件名排序的所有目录条目。
    //如果读取目录时发生错误，
    //ReadDir返回错误发生前能够读取的条目，
    //以及错误。
    private static List<DirectoryEntry> ReadDir(string name)
    {
        var dirInfo = new DirectoryInfo(name);
        var dirEntries = dirInfo.GetFileSystemInfos();

        var entries = dirEntries.Select(entry => new DirectoryEntry
        {
            Name = entry.Name,
            IsDirectory = entry.Attributes.HasFlag(FileAttributes.Directory),
            FullPath = entry.FullName
        }).ToList();
        // 按文件名对条目进行排序
        entries = entries.OrderBy(e => e.Name).ToList();
        return entries;
    }

    /// <summary>
    ///     扫描电影文件
    /// </summary>
    /// <param name="dir">文件地址</param>
    /// <param name="noSubPath">排除路径</param>
    /// <param name="log"></param>
    /// <returns></returns>
    private static async Task<List<Auto_Media>?> MatchedVideoFileAsync(string dir, List<string>? noSubPath, ILogger<VideoListHelper> log)
    {
        var fileFullPathList = new List<Auto_Media>();
        var pathSep = Path.DirectorySeparatorChar.ToString();
        // var files = Directory.GetDirectories(dir);
        var files = ReadDir(dir);
        foreach (var curFile in files)
        {
            if (noSubPath != null && noSubPath.Any(x => curFile.FullPath.Contains(x))) continue;
            // if (noSubPath != null && noSubPath.Contains(dir)) continue;
            var fullPath = dir + pathSep + curFile.Name;
            var directoryName = Path.GetDirectoryName(fullPath);
            var movieNameWithoutExt = Path.GetFileNameWithoutExtension(Path.GetFileName(fullPath));
            var metadateMvNfo = Path.Combine(directoryName, movieNameWithoutExt + Common.MetadateSeasonnNfo);
            var metadateMvNfo2 = Path.Combine(directoryName, Common.MetadateMVNfo);

            if (curFile.IsDirectory)
            {
                var oneList = await MatchedVideoFileAsync(fullPath, noSubPath, log);
                if (oneList != null) fileFullPathList.AddRange(oneList);
            }
            else
            {
                var fileNameIsBdmv = VideoFileUtils.FileNameIsBDMV(fullPath);
                if (fileNameIsBdmv.Item1)
                {
                    fileFullPathList.Add(new Auto_Media(BasicsHelper.GetId())
                    {
                        Name = Path.GetFileName(fileNameIsBdmv.Item2),
                        MediaType = GlobalVariableEnumConfiguration.MediaEnum.电影,
                        MainRootDirFPath = dir,
                        LastWriteTime = new FileInfo(fileNameIsBdmv.Item2).LastWriteTime,
                        Path = fileNameIsBdmv.Item2,
                        IsDisc = true,
                        InformationAddress = files.Find(x => x.FullPath == metadateMvNfo)?.FullPath ?? files.Find(x => x.FullPath == metadateMvNfo2)?.FullPath,
                        LandscapeAddress = FileHelper.GetMovieLandscape(fileNameIsBdmv.Item2),
                        PosterAddress = FileHelper.GetMoviePoster(fileNameIsBdmv.Item2),
                        Size = FileHelper.GetFolderSize(Path.GetDirectoryName(fileNameIsBdmv.Item2)!)
                    });
                    continue;
                }

                if (!VideoFileUtils.IsWantedVideoExtDef(curFile.Name)) continue;

                if (Path.GetFileName(directoryName) == "STREAM")
                {
                    log.LogDebug($"匹配的视频文件, 跳过BDMV.文件流: {fullPath}");
                    continue;
                }

                if (FileFilter.SkipFileInfo(log, curFile, fullPath)) continue;

                fileFullPathList.Add(new Auto_Media(BasicsHelper.GetId())
                {
                    Name = Path.GetFileName(fullPath),
                    MediaType = GlobalVariableEnumConfiguration.MediaEnum.电影,
                    MainRootDirFPath = dir,
                    LastWriteTime = new FileInfo(fullPath).LastWriteTime,
                    Path = fullPath,
                    InformationAddress = files.Find(x => x.FullPath == metadateMvNfo)?.FullPath ?? files.Find(x => x.FullPath == metadateMvNfo2)?.FullPath,
                    LandscapeAddress = FileHelper.GetMovieLandscape(fullPath),
                    PosterAddress = FileHelper.GetMoviePoster(fullPath),
                    Size = FileHelper.GetFileSize(fullPath)
                });
            }
        }

        return fileFullPathList.OrderByDescending(x => x.LastWriteTime).ToList();
    }

    #endregion
}